#ifndef LITENICE_3DPTR_HPP
#define LITENICE_3DPTR_HPP 1
#pragma once
#include <boost/shared_ptr.hpp>
#include <assert.h>

namespace ln
{
    template<typename T>
    class L3dPtr
    {
    public:
        L3dPtr()
        {
            m_lenX = boost::shared_ptr<int>(new int(0));
            m_lenY = boost::shared_ptr<int>(new int(0));
            m_lenZ = boost::shared_ptr<int>(new int(0));
            m_count = boost::shared_ptr<int>(new int(0));

            m_value = NULL;
        }

        L3dPtr(int dx,int dy,int dz)
        {
            m_lenX = boost::shared_ptr<int>(new int(dx));
            m_lenY = boost::shared_ptr<int>(new int(dy));
            m_lenZ = boost::shared_ptr<int>(new int(dz));
            m_count = boost::shared_ptr<int>(new int(1));

            m_value = new T [dx*dy*dz];
        }

        L3dPtr(const L3dPtr &rhs)
            :m_lenX(rhs.m_lenX)
            ,m_lenY(rhs.m_lenY)
            ,m_lenZ(rhs.m_lenZ)
            ,m_value(rhs.m_value)
            ,m_count(rhs.m_count)
        {
            if (*m_count != 0)
                ++(*m_count);
        }

        L3dPtr& operator = (const L3dPtr &rhs)
        {
            if (this == &rhs)
                return *this;

            dispose();

            m_value=rhs.m_value;
            m_lenX=rhs.m_lenX;
            m_lenY=rhs.m_lenY;
            m_lenZ=rhs.m_lenZ;
            m_count=rhs.m_count;

            ++(*m_count);

            return *this;
        }
 
        ~L3dPtr()
        {
            dispose();
        }
   
        const T& GetValue(int x,int y,int z) const 
        {
            assert(IsInArray(x,y,z));          
            return m_value[GetIndex(x, y, z)];
        }

        T& GetValue(int x, int y, int z)
        {
            assert(IsInArray(x, y, z));
            return m_value[GetIndex(x, y, z)];
        }

        const int GetLenX() const { return *m_lenX; }
        const int GetLenY() const { return *m_lenY; }
        const int GetLenZ() const { return *m_lenZ; }

        void SetValue(int x, int y, int z, const T &value)
        {
            assert(IsInArray(x, y, z));     
            m_value[GetIndex(x, y, z)] = value;
        }

        void SetSize(int len_x, int len_y, int len_z)
        {
            dispose();
            
            m_lenX = boost::shared_ptr<int>(new int(len_x));
            m_lenY = boost::shared_ptr<int>(new int(len_y));
            m_lenZ = boost::shared_ptr<int>(new int(len_z));
            m_count = boost::shared_ptr<int>(new int(1));

            int size = len_x*len_y*len_z;

            m_value = new T[size];
        }

        void SetAllValue(const T &value)
        {
            int sum = *m_lenX * *m_lenY * *m_lenZ;

            for (int i = 0; i < sum; ++i) {
                m_value[i] = value;
            }
        }
        
        bool IsInArray(int x, int y ,int z) const 
        {
            if (x<0 ||x>*m_lenX-1 || 
                y<0||y>*m_lenY-1||
                z<0||z>*m_lenZ-1)
                return false;
            return true;
        }

    private:
        void dispose()
        {
            if(*m_count==0)
                return;

            --(*m_count);

            if (*m_count==0) {
                if (m_value) {
                    delete [] m_value;
                    m_value = NULL;
                }
            } // end if *m_count == 0
        } // end function dispose();

        int GetIndex(int x, int y, int z) const
        {
            return z * (*m_lenX * *m_lenY) + y * (*m_lenX) + x;
        }

    private:
        boost::shared_ptr<int> m_count;
        boost::shared_ptr<int> m_lenX;
        boost::shared_ptr<int> m_lenY;
        boost::shared_ptr<int> m_lenZ;
        T *m_value;
    }; //end class
}

#endif